/*
 * Пакет "Экосистема"
 * реализует модель экологической системы
 */
package vitarium.ecosystem;

import java.util.ArrayList;
import java.util.LinkedList;
import vitarium.ecosystem.events.*;

/** Класс Консументов: травоядных и хищников*/
public class Consumens extends BiocoenosisElement {

    /** Дальность видимости */
    private int viewRange;
    /** Возможные клетки для передвижения */
    LinkedList<Location> possibleMovementsList;

    public Consumens(EcoSystem world, int longitude, int latitude, int organics, String imagePath, int viewRange) {
        super(world, longitude, latitude, 0, organics, 0, imagePath);
        diet = new LinkedList();
        this.viewRange = viewRange;

        this.addEdibleObjectListener(new EdibleElementFoundListener() {

            public void edibleObjectFound(EdibleElementFoundEvent edievent) {
                edievent.consumeTactics();
            }
        });

        this.addCompetitorElementFoundListener(new CompetitorElementFoundListener() {

            public void сompetitorElementFound(CompetitorElementFoundEvent compevent) {
                compevent.competitorTactics();
            }
        });
    }

    /** Возвращает дальность видимости */
    public int getViewRange() {
        return viewRange;
    }

    /** Логика поведения */
    public void live() {
        lookAround(viewRange);
        moveRandom();
    }

    /** Поглощение */
    public void eat(int foodLongitude, int foodLatitude) {
        setLongitude(foodLongitude);
        setLatitude(foodLatitude);
        /** Ход совершен */
        possibleMovementsList = null;
        this.fireElementEaten(world.getElement(foodLongitude, foodLatitude));
    }

    /** Перемещает объект от клетки с указанными координатами */
    public void moveOutFrom(int fromLongitude, int fromLatitude) {

        if (this.longitude < fromLongitude) {
            setLongitude(this.longitude - 1);
        } else {
            setLongitude(this.longitude + 1);
        }

        if (this.latitude < fromLatitude) {
            setLatitude(this.latitude - 1);
        } else {
            setLatitude(this.latitude + 1);
        }
        /** Ход совершен */
        possibleMovementsList = null;
    }

    /** Изменение координат объекта случайным образом */
    public void moveRandom() {

        /** Количество возможностей */
        try {
            int possibilitiesNumber = possibleMovementsList.size();
            if (possibilitiesNumber > 0) {
                /** Случайно выбираем направление для дальнейшего движения */
                int randomIndex = (int) (Math.round(Math.random()) * (possibilitiesNumber - 1));
                Location destination = possibleMovementsList.get(randomIndex);
                setLongitude(destination.x);
                setLatitude(destination.y);
            }
            /** Ход совершен */
            possibleMovementsList = null;

        } catch (NullPointerException ex) {
        }

    }

    /** Просмотр окрестностей  */
    public void lookAround(int searchRadius) {
        int westernBorder, easternBorder, northernBorder, southernBorder;

        possibleMovementsList = new LinkedList<Location>();

        /* Задание области для поиска */
        /** Западная граница  */
        if (this.longitude - searchRadius >= 0) {
            westernBorder = this.longitude - searchRadius;
        } else {
            westernBorder = this.longitude;
        }
        /** Восточная граница  */
        if (this.longitude + searchRadius < this.world.getSizeFromWestToEast()) {
            easternBorder = this.longitude + searchRadius;
        } else {
            easternBorder = this.longitude;
        }
        /** Северная граница  */
        if (this.latitude - searchRadius >= 0) {
            northernBorder = this.latitude - searchRadius;
        } else {
            northernBorder = this.latitude;
        }
        /** Южная граница  */
        if (this.latitude + searchRadius < this.world.getSizeFromNorthToSouth()) {
            southernBorder = this.latitude + searchRadius;
        } else {
            southernBorder = this.latitude;
        }

        /** Просматриваем окрестности
         *
         * Последовательность просмотра
         * NW - N - NE - W -E - SW - S -SE
         * 
         */
        for (int i = westernBorder; i <= easternBorder; i++) {
            for (int j = northernBorder; j <= southernBorder; j++) {

                /* Проверка клетки */
                if (!WaterBasin.class.equals(world.getEdaphotope(i, j).getClass())) {

                    /* Не проверям содержимое текущей клетки */
                    if (!(i == longitude & j == longitude)) {
                        /* Смотрим на объект с координатами i , j */
                        try {
                            BiocoenosisElement found = this.world.getElement(i, j);

                            /* Проверка объекта на съедобность */
                            if (found.isEdible(this)) {
                                this.fireEdibleElementFound(found);
                                return;
                            }
                            /* Проверка объекта на опасность */
                            if (found.isDangerous(this)) {
                                this.fireDangerousElementFound(found);
                                return;
                            }
                            /* Проверка объекта на конкурентность */
                            if (found.currentList == this.currentList) {
                            }

                        } catch (NullPointerException noObject) {

                            possibleMovementsList.add(new Location(i, j));
                        }
                    }
                }
            }
        }
    }
    /** CОБЫТИЯ */
    /** Обнаружен опасный объект */
    /** Список слушателей события обнаружения опасного объекта */
    private ArrayList<DangerousElementFoundListener> dangerListeners = new ArrayList<DangerousElementFoundListener>();

    /** Добавление слушателя событию обнаружения опасного объекта */
    public void addDangerousElementFoundListener(DangerousElementFoundListener listener) {
        dangerListeners.add(listener);
    }

    /** Возвращает массив слушателей события обнаружения опасного объекта  */
    public DangerousElementFoundListener[] getDangerousObjectListeners() {
        return dangerListeners.toArray(new DangerousElementFoundListener[dangerListeners.size()]);
    }

    /** Удаляет слушателя события обнаружения опасного объекта */
    public void removeDangerousObjectListener(DangerousElementFoundListener listener) {
        dangerListeners.remove(listener);
    }

    /** Инициирует событие и оповещает слушателей при обнаружении опасного объекта  */
    protected void fireDangerousElementFound(BiocoenosisElement dangerousObject) {
        DangerousElementFoundEvent ev = new DangerousElementFoundEvent(this, dangerousObject);
        for (DangerousElementFoundListener listener : dangerListeners) {
            listener.dangerousObjectFound(ev);
        }
    }
    /** Обнаружен съедобный объект */
    /** Список слушателей события обнаружения съедобного объекта */
    private ArrayList<EdibleElementFoundListener> foodListeners = new ArrayList<EdibleElementFoundListener>();

    /** Добавление слушателя событию обнаружения съедобного объекта */
    public void addEdibleObjectListener(EdibleElementFoundListener listener) {
        foodListeners.add(listener);
    }

    /** Возвращает массив слушателей события обнаружения съедобного объекта  */
    public EdibleElementFoundListener[] getEdibleObjectListeners() {
        return foodListeners.toArray(new EdibleElementFoundListener[foodListeners.size()]);
    }

    /** Удаляет слушателя события обнаружения съедобного объекта */
    public void removeEdibleObjectListener(EdibleElementFoundListener listener) {
        foodListeners.remove(listener);
    }

    /** Инициирует событие и оповещает слушателей при обнаружении съедобного объекта  */
    protected void fireEdibleElementFound(BiocoenosisElement edibleObject) {
        EdibleElementFoundEvent ev = new EdibleElementFoundEvent(this, edibleObject);
        for (EdibleElementFoundListener listener : foodListeners) {
            listener.edibleObjectFound(ev);
        }
    }
    /** Обнаружен объект-конкурент*/
    /** Список слушателей события обнаружения конкурентного объекта */
    private ArrayList<CompetitorElementFoundListener> сompetitorListeners = new ArrayList<CompetitorElementFoundListener>();

    /** Добавление слушателя событию обнаружения конкурентного объекта */
    public void addCompetitorElementFoundListener(CompetitorElementFoundListener listener) {
        сompetitorListeners.add(listener);
    }

    /** Возвращает массив слушателей события обнаружения конкурентного объекта  */
    public CompetitorElementFoundListener[] getCompetitorElementListeners() {
        return сompetitorListeners.toArray(new CompetitorElementFoundListener[сompetitorListeners.size()]);
    }

    /** Удаляет слушателя события обнаружения конкурентного объекта */
    public void removeCompetitorElementFoundListener(CompetitorElementFoundListener listener) {
        сompetitorListeners.remove(listener);
    }

    /** Инициирует событие и оповещает слушателей при обнаружении конкурентного объекта  */
    protected void fireCompetitorElementFound(BiocoenosisElement competitorElement) {
        CompetitorElementFoundEvent ev = new CompetitorElementFoundEvent(this, competitorElement);
        for (CompetitorElementFoundListener listener : сompetitorListeners) {
            listener.сompetitorElementFound(ev);
        }
    }
}
